import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext, ttk, colorchooser, font
import os
from datetime import datetime
from collections import deque
import platform
import re
import json

class EnhancedNotepad:
    def __init__(self, root):
        self.root = root
        self.root.title("增强记事本 - 未命名")
        self.root.geometry("1200x700")

        # 基本属性
        self.current_file = None
        self.is_modified = False
        self.recent_files = []
        self.max_recent = 5

        # 增强功能
        self.clipboard_history = deque(maxlen=10)
        self.default_font = self.get_default_font()
        self.current_color = "#000000"
        self.find_dialog = None

        # 文件树
        self.project_dir = None
        self.tree_data = {}

        # 格式变量
        self.bold_var = tk.BooleanVar()
        self.italic_var = tk.BooleanVar()
        self.underline_var = tk.BooleanVar()

        # 初始化
        self.setup_ui()
        self.setup_bindings()

    def get_default_font(self):
        system = platform.system()
        if system == "Windows":
            return ("Microsoft YaHei UI", 12)
        elif system == "Darwin":  # macOS
            return ("PingFang SC", 13)
        else:  # Linux
            return ("Noto Sans CJK SC", 12)

    def setup_ui(self):
        main_frame = tk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True)

        # ========== 左侧：文件树 ==========
        left_panel = tk.Frame(main_frame, width=250, bg="#f0f0f0")
        left_panel.pack(side=tk.LEFT, fill=tk.Y, padx=2, pady=2)
        left_panel.pack_propagate(False)

        tk.Label(left_panel, text="项目文件", bg="#f0f0f0", font=("SimHei", 10, "bold")).pack(pady=5)

        tree_frame = tk.Frame(left_panel)
        tree_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        self.tree = ttk.Treeview(tree_frame, show="tree", selectmode="browse")
        self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        v_scroll = ttk.Scrollbar(tree_frame, orient="vertical", command=self.tree.yview)
        v_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.tree.configure(yscrollcommand=v_scroll.set)

        # 右键菜单
        self.tree_menu = tk.Menu(self.root, tearoff=0)
        self.tree_menu.add_command(label="新建文件", command=self.create_new_file)
        self.tree_menu.add_command(label="新建文件夹", command=self.create_new_folder)
        self.tree_menu.add_command(label="重命名", command=self.rename_item)
        self.tree_menu.add_command(label="删除", command=self.delete_item)

        self.tree.bind("<Button-3>", self.show_tree_menu)
        self.tree.bind("<Double-1>", self.on_tree_double_click)

        btn_frame = tk.Frame(left_panel)
        btn_frame.pack(fill=tk.X, pady=5)
        ttk.Button(btn_frame, text="打开项目", command=self.open_project).pack(fill=tk.X)
        ttk.Button(btn_frame, text="保存项目", command=self.save_project_state).pack(fill=tk.X, pady=2)

        # ========== 右侧：主界面 ==========
        right_frame = tk.Frame(main_frame)
        right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=2, pady=2)

        # 工具栏
        self.create_toolbar(right_frame)

        # 菜单栏
        self.setup_menu()

        # 文本区域
        self.text_area = scrolledtext.ScrolledText(
            right_frame,
            wrap=tk.WORD,
            font=self.default_font,
            undo=True,
            fg=self.current_color
        )
        self.text_area.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 状态栏
        self.status_bar = tk.Label(self.root, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)

        # 绑定事件
        self.text_area.bind("<<Modified>>", self.on_text_modified)
        self.text_area.bind("<ButtonRelease-1>", self.update_format_toolbar)
        self.text_area.bind("<KeyRelease>", self.update_format_toolbar)
        self.text_area.bind("<ButtonRelease-3>", self.update_format_toolbar)

    def create_toolbar(self, parent):
        toolbar = ttk.Frame(parent, padding="5")
        toolbar.pack(side=tk.TOP, fill=tk.X)

        ttk.Button(toolbar, text="撤销", command=self.undo).pack(side=tk.LEFT, padx=2)
        ttk.Button(toolbar, text="重做", command=self.redo).pack(side=tk.LEFT, padx=2)
        ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)

        ttk.Button(toolbar, text="剪切", command=self.cut).pack(side=tk.LEFT, padx=2)
        ttk.Button(toolbar, text="复制", command=self.copy).pack(side=tk.LEFT, padx=2)
        ttk.Button(toolbar, text="粘贴", command=self.paste).pack(side=tk.LEFT, padx=2)
        ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)

        ttk.Checkbutton(toolbar, text="粗体", command=self.toggle_bold, variable=self.bold_var).pack(side=tk.LEFT, padx=2)
        ttk.Checkbutton(toolbar, text="斜体", command=self.toggle_italic, variable=self.italic_var).pack(side=tk.LEFT, padx=2)
        ttk.Checkbutton(toolbar, text="下划线", command=self.toggle_underline, variable=self.underline_var).pack(side=tk.LEFT, padx=2)

        ttk.Button(toolbar, text="颜色", command=self.choose_text_color).pack(side=tk.LEFT, padx=2)
        ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)

        ttk.Button(toolbar, text="查找/替换", command=self.show_find_replace).pack(side=tk.LEFT, padx=2)

    def setup_menu(self):
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)

        # 文件菜单
        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="文件", menu=file_menu)
        file_menu.add_command(label="新建", command=self.new_file, accelerator="Ctrl+N")
        file_menu.add_command(label="打开", command=self.open_file, accelerator="Ctrl+O")
        file_menu.add_separator()
        file_menu.add_command(label="保存", command=self.save_file, accelerator="Ctrl+S")
        file_menu.add_command(label="另存为", command=self.save_as_file, accelerator="Ctrl+Shift+S")
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.exit_app, accelerator="Ctrl+Q")

        # 编辑菜单
        edit_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="编辑", menu=edit_menu)
        edit_menu.add_command(label="撤销", command=self.undo, accelerator="Ctrl+Z")
        edit_menu.add_command(label="重做", command=self.redo, accelerator="Ctrl+Y")
        edit_menu.add_separator()
        edit_menu.add_command(label="剪切", command=self.cut, accelerator="Ctrl+X")
        edit_menu.add_command(label="复制", command=self.copy, accelerator="Ctrl+C")
        edit_menu.add_command(label="粘贴", command=self.paste, accelerator="Ctrl+V")
        edit_menu.add_separator()
        edit_menu.add_command(label="全选", command=self.select_all, accelerator="Ctrl+A")
        edit_menu.add_command(label="查找/替换", command=self.show_find_replace, accelerator="Ctrl+F")

        # 格式菜单
        format_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="格式", menu=format_menu)

        self.font_menu = tk.Menu(format_menu, tearoff=0)
        format_menu.add_cascade(label="字体", menu=self.font_menu)
        self.populate_font_menu()

        self.size_menu = tk.Menu(format_menu, tearoff=0)
        format_menu.add_cascade(label="字号", menu=self.size_menu)
        for size in [8, 10, 12, 14, 16, 18, 20, 24, 28, 32]:
            self.size_menu.add_command(label=str(size), command=lambda s=size: self.change_font_size(s))

        format_menu.add_separator()
        format_menu.add_command(label="粗体", command=self.toggle_bold, accelerator="Ctrl+B")
        format_menu.add_command(label="斜体", command=self.toggle_italic, accelerator="Ctrl+I")
        format_menu.add_command(label="下划线", command=self.toggle_underline, accelerator="Ctrl+U")
        format_menu.add_command(label="删除线", command=self.toggle_strikethrough)
        format_menu.add_separator()
        format_menu.add_command(label="文字颜色", command=self.choose_text_color)

    def populate_font_menu(self):
        
        try:
            all_fonts = font.families()
            # 过滤掉以 @ 开头的字体（这些是竖排字体）
            fonts = [f for f in all_fonts if not f.startswith("@")]
            # 可选：排序并限制数量
            fonts = sorted(set(fonts))[:100]  # 取前100个常用字体
        except:
            # 备用字体列表
            fonts = ["SimHei", "Microsoft YaHei", "SimSun", "KaiTi", "FangSong",
                     "Arial", "Times New Roman", "Courier New", "Consolas"]

        for f in fonts:
            self.font_menu.add_command(
                label=f,
                command=lambda font=f: self.change_font(font)
            )

    def setup_bindings(self):
        if hasattr(self, '_bindings_set'):
            return
        self._bindings_set = True

        bindings = [
            ("<Control-n>", self.new_file),
            ("<Control-o>", self.open_file),
            ("<Control-s>", self.save_file),
            ("<Control-Shift-S>", self.save_as_file),
            ("<Control-q>", self.exit_app),
            ("<Control-z>", self.undo),
            ("<Control-y>", self.redo),
            ("<Control-x>", self.cut),
            ("<Control-c>", self.copy),
            ("<Control-v>", self.paste),
            ("<Control-a>", self.select_all),
            ("<Control-f>", self.show_find_replace),
            ("<Control-b>", self.toggle_bold),
            ("<Control-i>", self.toggle_italic),
            ("<Control-u>", self.toggle_underline),
        ]
        for key, cmd in bindings:
            self.root.bind(key, lambda e, c=cmd: c())

    def on_text_modified(self, event):
        if self.text_area.edit_modified():
            self.is_modified = True
            self.update_title()
        self.text_area.edit_modified(False)

    def update_title(self):
        filename = os.path.basename(self.current_file) if self.current_file else "未命名"
        modified = "*" if self.is_modified else ""
        self.root.title(f"增强记事本 - {filename}{modified}")

    def update_status(self, message):
        self.status_bar.config(text=message)

    def new_file(self):
        if self.check_save():
            self.text_area.delete(1.0, tk.END)
            self.current_file = None
            self.is_modified = False
            self.text_area.edit_reset()
            self.update_title()
            self.update_status("新建文件")

    def open_file(self, filepath=None):
        if self.check_save():
            if not filepath:
                file_types = [("文本文件", "*.txt"), ("所有文件", "*.*")]
                filepath = filedialog.askopenfilename(filetypes=file_types)
            if filepath:
                try:
                    with open(filepath, 'r', encoding='utf-8') as file:
                        content = file.read().rstrip('\n')
                    self.text_area.delete(1.0, tk.END)
                    self.text_area.insert(1.0, content)
                    self.current_file = filepath
                    self.is_modified = False
                    self.text_area.edit_reset()
                    self.update_title()
                    self.add_to_recent(filepath)
                    self.update_status(f"已打开: {os.path.basename(filepath)}")
                    self.select_tree_item_by_path(filepath)
                except Exception as e:
                    messagebox.showerror("错误", f"无法打开文件:\n{str(e)}")

    def save_file(self):
        if self.current_file:
            try:
                content = self.text_area.get(1.0, tk.END + "-1c")
                with open(self.current_file, 'w', encoding='utf-8') as file:
                    file.write(content)
                self.is_modified = False
                self.update_title()
                self.update_status(f"已保存: {os.path.basename(self.current_file)}")
                return True
            except Exception as e:
                messagebox.showerror("错误", f"保存失败:\n{str(e)}")
                return False
        else:
            return self.save_as_file()

    def save_as_file(self):
        file_types = [("文本文件", "*.txt"), ("所有文件", "*.*")]
        filepath = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=file_types)
        if filepath:
            try:
                content = self.text_area.get(1.0, tk.END + "-1c")
                with open(filepath, 'w', encoding='utf-8') as file:
                    file.write(content)
                self.current_file = filepath
                self.is_modified = False
                self.update_title()
                self.add_to_recent(filepath)
                self.update_status(f"已另存为: {os.path.basename(filepath)}")
                self.add_file_to_tree(filepath)
                return True
            except Exception as e:
                messagebox.showerror("错误", f"保存失败:\n{str(e)}")
                return False
        return False

    def check_save(self):
        if self.is_modified:
            result = messagebox.askyesnocancel("保存更改", "文件已修改，是否保存更改？")
            if result is None: return False
            elif result: return self.save_file()
            else: return True
        return True

    def exit_app(self):
        if self.check_save():
            if self.find_dialog and self.find_dialog.winfo_exists():
                self.find_dialog.destroy()
            self.root.quit()

    def add_to_recent(self, filepath):
        if filepath in self.recent_files:
            self.recent_files.remove(filepath)
        self.recent_files.insert(0, filepath)
        self.recent_files = self.recent_files[:self.max_recent]

    # ========== 文件树功能 ==========
    def open_project(self):
        path = filedialog.askdirectory(title="选择项目文件夹")
        if path:
            self.project_dir = path
            self.tree_data = {}
            self.tree.delete(*self.tree.get_children())
            self.load_directory_tree(path, "")

    def load_directory_tree(self, path, parent_id):
        name = os.path.basename(path)
        item_id = self.tree.insert(parent_id, "end", text=name, open=True)
        self.tree_data[item_id] = {"path": path, "type": "folder", "parent": parent_id}

        if os.path.isdir(path):
            try:
                for entry in sorted(os.listdir(path)):
                    entry_path = os.path.join(path, entry)
                    if os.path.isdir(entry_path):
                        self.load_directory_tree(entry_path, item_id)
                    elif entry.endswith(".txt"):
                        file_id = self.tree.insert(item_id, "end", text=entry)
                        self.tree_data[file_id] = {"path": entry_path, "type": "file", "parent": item_id}
            except PermissionError:
                pass

    def select_tree_item_by_path(self, filepath):
        for item_id, data in self.tree_data.items():
            if data.get("path") == filepath:
                self.tree.selection_set(item_id)
                self.tree.see(item_id)
                break

    def add_file_to_tree(self, filepath):
        dirpath = os.path.dirname(filepath)
        parent_id = ""
        for item_id, data in self.tree_data.items():
            if data.get("path") == dirpath and data["type"] == "folder":
                parent_id = item_id
                break
        item_id = self.tree.insert(parent_id, "end", text=os.path.basename(filepath))
        self.tree_data[item_id] = {"path": filepath, "type": "file", "parent": parent_id}

    def show_tree_menu(self, event):
        item = self.tree.identify_row(event.y)
        if item:
            self.tree.selection_set(item)
            self.tree_menu.post(event.x_root, event.y_root)

    def on_tree_double_click(self, event):
        item = self.tree.selection()
        if not item: return
        data = self.tree_data.get(item[0])
        if data and data["type"] == "file":
            self.open_file(data["path"])

    def create_new_file(self):
        parent = self.tree.focus()
        parent_data = self.tree_data.get(parent, {"path": self.project_dir})
        parent_path = parent_data["path"] if parent_data["type"] == "folder" else os.path.dirname(parent_data["path"])
        name = tk.simpledialog.askstring("新建文件", "输入文件名:", initialvalue="新文件.txt")
        if name:
            path = os.path.join(parent_path, name)
            with open(path, 'w', encoding='utf-8') as f:
                f.write("")
            item_id = self.tree.insert(parent, "end", text=name)
            self.tree_data[item_id] = {"path": path, "type": "file", "parent": parent}
            self.update_status(f"已创建: {name}")

    def create_new_folder(self):
        parent = self.tree.focus()
        parent_data = self.tree_data.get(parent, {"path": self.project_dir})
        parent_path = parent_data["path"] if parent_data["type"] == "folder" else os.path.dirname(parent_data["path"])
        name = tk.simpledialog.askstring("新建文件夹", "输入名称:")
        if name:
            path = os.path.join(parent_path, name)
            os.makedirs(path, exist_ok=True)
            item_id = self.tree.insert(parent, "end", text=name, open=True)
            self.tree_data[item_id] = {"path": path, "type": "folder", "parent": parent}
            self.update_status(f"已创建: {name}")

    def rename_item(self):
        item = self.tree.focus()
        if not item: return
        old_text = self.tree.item(item, "text")
        new_text = tk.simpledialog.askstring("重命名", "新名称:", initialvalue=old_text)
        if new_text and new_text != old_text:
            data = self.tree_data[item]
            old_path = data["path"]
            new_path = os.path.join(os.path.dirname(old_path), new_text)
            try:
                os.rename(old_path, new_path)
                self.tree.item(item, text=new_text)
                data["path"] = new_path
                if data["type"] == "file" and self.current_file == old_path:
                    self.current_file = new_path
                    self.update_title()
                self.update_status(f"已重命名: {old_text} → {new_text}")
            except Exception as e:
                messagebox.showerror("错误", str(e))

    def delete_item(self):
        item = self.tree.focus()
        if not item: return
        data = self.tree_data[item]
        if messagebox.askyesno("确认删除", f"删除 {data['path']}？", icon='warning'):
            try:
                if data["type"] == "folder":
                    import shutil
                    shutil.rmtree(data["path"])
                else:
                    os.remove(data["path"])
                del self.tree_data[item]
                self.tree.delete(item)
                self.update_status("已删除")
            except Exception as e:
                messagebox.showerror("错误", str(e))

    def save_project_state(self):
        if not self.project_dir:
            messagebox.showwarning("提示", "请先打开项目")
            return
        config_path = os.path.join(self.project_dir, ".notepad.json")
        state = {"files": [d["path"] for d in self.tree_data.values() if d["type"] == "file"]}
        try:
            with open(config_path, 'w', encoding='utf-8') as f:
                json.dump(state, f, indent=2, ensure_ascii=False)
            messagebox.showinfo("成功", "项目状态已保存")
        except Exception as e:
            messagebox.showerror("错误", f"保存失败: {str(e)}")

    # ========== 格式化功能（已修复）==========
    def toggle_bold(self):
        try:
            start = self.text_area.index(tk.SEL_FIRST)
        except tk.TclError:
            messagebox.showinfo("提示", "请先选择要设置格式的文本")
            return

        if "my_bold" in self.text_area.tag_names(start):
            self.text_area.tag_remove("my_bold", tk.SEL_FIRST, tk.SEL_LAST)
            self.bold_var.set(False)
            self.update_status("已取消粗体")
        else:
            current_font = font.Font(font=self.text_area["font"])
            bold_font = (current_font.actual()['family'], current_font.actual()['size'], "bold")
            self.text_area.tag_configure("my_bold", font=bold_font)
            self.text_area.tag_add("my_bold", tk.SEL_FIRST, tk.SEL_LAST)
            self.bold_var.set(True)
            self.update_status("已设置粗体")

    def toggle_italic(self):
        try:
            start = self.text_area.index(tk.SEL_FIRST)
        except tk.TclError:
            messagebox.showinfo("提示", "请先选择要设置格式的文本")
            return

        if "my_italic" in self.text_area.tag_names(start):
            self.text_area.tag_remove("my_italic", tk.SEL_FIRST, tk.SEL_LAST)
            self.italic_var.set(False)
            self.update_status("已取消斜体")
        else:
            current_font = font.Font(font=self.text_area["font"])
            italic_font = (current_font.actual()['family'], current_font.actual()['size'], "italic")
            self.text_area.tag_configure("my_italic", font=italic_font)
            self.text_area.tag_add("my_italic", tk.SEL_FIRST, tk.SEL_LAST)
            self.italic_var.set(True)
            self.update_status("已设置斜体")

    def toggle_underline(self):
        try:
            start = self.text_area.index(tk.SEL_FIRST)
        except tk.TclError:
            messagebox.showinfo("提示", "请先选择要设置格式的文本")
            return

        if "my_underline" in self.text_area.tag_names(start):
            self.text_area.tag_remove("my_underline", tk.SEL_FIRST, tk.SEL_LAST)
            self.underline_var.set(False)
            self.update_status("已取消下划线")
        else:
            self.text_area.tag_configure("my_underline", underline=True)
            self.text_area.tag_add("my_underline", tk.SEL_FIRST, tk.SEL_LAST)
            self.underline_var.set(True)
            self.update_status("已设置下划线")

    def toggle_strikethrough(self):
        try:
            start = self.text_area.index(tk.SEL_FIRST)
        except tk.TclError:
            messagebox.showinfo("提示", "请先选择要设置格式的文本")
            return

        if "my_overstrike" in self.text_area.tag_names(start):
            self.text_area.tag_remove("my_overstrike", tk.SEL_FIRST, tk.SEL_LAST)
            self.update_status("已取消删除线")
        else:
            self.text_area.tag_configure("my_overstrike", overstrike=True)
            self.text_area.tag_add("my_overstrike", tk.SEL_FIRST, tk.SEL_LAST)
            self.update_status("已设置删除线")

    def choose_text_color(self):
        color = colorchooser.askcolor(title="选择文字颜色")[1]
        if color:
            try:
                start = self.text_area.index(tk.SEL_FIRST)
                end = self.text_area.index(tk.SEL_LAST)
                tag_name = f"color_{color.replace('#', '_')}"
                self.text_area.tag_configure(tag_name, foreground=color)
                self.text_area.tag_add(tag_name, start, end)
                self.update_status("已更改文字颜色")
            except tk.TclError:
                messagebox.showinfo("提示", "请先选择要更改颜色的文本")

    def change_font_size(self, size):
        try:
            start = self.text_area.index(tk.SEL_FIRST)
            end = self.text_area.index(tk.SEL_LAST)
            current_font = font.Font(font=self.text_area["font"])
            tag_name = f"size_{size}"
            self.text_area.tag_configure(tag_name, font=(current_font.actual()['family'], size))
            self.text_area.tag_add(tag_name, start, end)
            self.update_status(f"字号已改为 {size}")
        except tk.TclError:
            messagebox.showinfo("提示", "请先选择要设置格式的文本")

    def change_font(self, font_name):
        try:
            start = self.text_area.index(tk.SEL_FIRST)
            end = self.text_area.index(tk.SEL_LAST)
            current_font = font.Font(font=self.text_area["font"])
            tag_name = f"font_{font_name}"
            self.text_area.tag_configure(tag_name, font=(font_name, current_font.actual()['size']))
            self.text_area.tag_add(tag_name, start, end)
            self.update_status(f"字体已改为 {font_name}")
        except tk.TclError:
            messagebox.showinfo("提示", "请先选择要设置格式的文本")

    def update_format_toolbar(self, event=None):
        """更新工具栏按钮状态"""
        try:
            index = self.text_area.index(tk.INSERT)
            tags = self.text_area.tag_names(index)
            self.bold_var.set("my_bold" in tags)
            self.italic_var.set("my_italic" in tags)
            self.underline_var.set("my_underline" in tags)
        except:
            pass

    # ========== 查找替换 ==========
    def show_find_replace(self):
        if hasattr(self, 'find_dialog') and self.find_dialog and self.find_dialog.winfo_exists():
            self.find_dialog.lift()
            return
        self.create_find_replace_dialog()

    def create_find_replace_dialog(self):
        self.find_dialog = tk.Toplevel(self.root)
        self.find_dialog.title("查找和替换")
        self.find_dialog.geometry("400x200")
        self.find_dialog.transient(self.root)
        self.find_dialog.resizable(False, False)

        self.find_text = tk.StringVar()
        self.replace_text = tk.StringVar()
        self.case_var = tk.BooleanVar()

        frame = ttk.Frame(self.find_dialog, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)

        ttk.Label(frame, text="查找内容:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.find_entry = ttk.Entry(frame, width=30, textvariable=self.find_text)
        self.find_entry.grid(row=0, column=1, pady=5)
        self.find_entry.focus()

        ttk.Label(frame, text="替换为:").grid(row=1, column=0, sticky=tk.W, pady=5)
        self.replace_entry = ttk.Entry(frame, width=30, textvariable=self.replace_text)
        self.replace_entry.grid(row=1, column=1, pady=5)

        ttk.Checkbutton(frame, text="匹配大小写", variable=self.case_var).grid(row=2, column=0, sticky=tk.W)

        button_frame = ttk.Frame(frame)
        button_frame.grid(row=3, column=0, columnspan=2, pady=10)

        ttk.Button(button_frame, text="查找下一个", command=self.find_next).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="替换", command=self.replace_current).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="全部替换", command=self.replace_all).pack(side=tk.LEFT, padx=5)

        self.find_entry.bind("<Return>", lambda e: self.find_next())
        self.replace_entry.bind("<Return>", lambda e: self.find_next())

        def on_close():
            self.find_dialog.destroy()
            self.find_dialog = None
        self.find_dialog.protocol("WM_DELETE_WINDOW", on_close)

    def find_next(self):
        search = self.find_text.get()
        if not search: return
        start = self.text_area.index(tk.INSERT) if self.text_area.tag_ranges(tk.SEL) else self.text_area.index("1.0")
        opts = {} if self.case_var.get() else {"nocase": True}
        pos = self.text_area.search(search, start, stopindex=tk.END, **opts)
        if not pos:
            pos = self.text_area.search(search, "1.0", stopindex=start, **opts)
        if pos:
            end = f"{pos}+{len(search)}c"
            self.text_area.tag_remove(tk.SEL, "1.0", tk.END)
            self.text_area.tag_add(tk.SEL, pos, end)
            self.text_area.mark_set(tk.INSERT, end)
            self.text_area.see(pos)
            self.update_status(f"找到: {search}")
        else:
            self.update_status(f"未找到: {search}")

    def replace_current(self):
        search = self.find_text.get()
        replace = self.replace_text.get()
        if not search: return
        try:
            selected = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
            match = selected == search if self.case_var.get() else selected.lower() == search.lower()
            if match:
                self.text_area.delete(tk.SEL_FIRST, tk.SEL_LAST)
                self.text_area.insert(tk.SEL_FIRST, replace)
                self.update_status("已替换")
            self.find_next()
        except tk.TclError:
            self.find_next()

    def replace_all(self):
        search = self.find_text.get()
        replace = self.replace_text.get()
        if not search: return
        content = self.text_area.get("1.0", tk.END + "-1c")
        flags = 0 if self.case_var.get() else re.IGNORECASE
        try:
            compiled = re.compile(re.escape(search), flags)
            new_content, count = compiled.subn(replace, content)
            if count > 0:
                self.text_area.delete("1.0", tk.END)
                self.text_area.insert("1.0", new_content)
                self.update_status(f"已替换 {count} 处")
            else:
                self.update_status(f"未找到: {search}")
        except Exception as e:
            messagebox.showerror("错误", str(e))

    # ========== 编辑操作 ==========
    def undo(self):
        try:
            self.text_area.edit_undo()
            self.update_status("已撤销")
        except tk.TclError:
            self.update_status("没有可撤销的操作")

    def redo(self):
        try:
            self.text_area.edit_redo()
            self.update_status("已重做")
        except tk.TclError:
            self.update_status("没有可重做的操作")

    def cut(self):
        try:
            selected = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
            self.clipboard_history.append(selected)
            self.update_clipboard_menu()
            self.text_area.event_generate("<<Cut>>")
            self.update_status("已剪切")
        except tk.TclError:
            self.update_status("未选择任何内容")

    def copy(self):
        try:
            selected = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
            self.clipboard_history.append(selected)
            self.update_clipboard_menu()
            self.text_area.event_generate("<<Copy>>")
            self.update_status("已复制")
        except tk.TclError:
            self.update_status("未选择任何内容")

    def paste(self):
        self.text_area.event_generate("<<Paste>>")
        self.update_status("已粘贴")

    def select_all(self):
        self.text_area.tag_add(tk.SEL, "1.0", tk.END)
        self.text_area.mark_set(tk.INSERT, tk.END)
        self.text_area.see(tk.INSERT)
        self.update_status("已全选")

    def update_clipboard_menu(self):
        pass  # 暂不实现剪贴板历史菜单


if __name__ == "__main__":
    root = tk.Tk()
    app = EnhancedNotepad(root)
    root.mainloop()